﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using VsxFactory.Modeling.Strategies;
using VsxFactory.Modeling.VisualStudio;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Modeling.Shell;
using VsxFactory.Modeling.Repository;
using System.Xml.Linq;
using Microsoft.VisualStudio.Modeling;

namespace VsxFactory.Modeling.StrategyManager.Strategies
{
    [StrategyProvider(StrategyProviderAttribute.StandardStrategy)]
    class DependentFileStrategyProvider : IStrategyProvider
    {
        private bool IsExtenderProviderInitialized;
        private List<int> extenderProviderCookies = new List<int>();
        public IServiceProvider ServiceProvider { get; private set; }
        private IStrategiesHost host;

        [ImportMany("Strategy")]
        public IEnumerable<Lazy<IGenerationStrategy, IStrategyMetadata>> Strategies { get; set; }

        /// <summary>
        /// Create a strategy for a document
        /// </summary>
        /// <param name="document">A document from the current solution</param>
        /// <returns>A new strategy or null</returns>
        public IStrategy CreateStrategy(IVsProjectFileItem item, Store store)
        {
            System.Diagnostics.Contracts.Contract.Requires(item != null);

            IServiceProvider sp = ServiceProvider;

            // Recherche si une stratégie a été définie dans les propriétés du modéle.
            // La stratégie est définie dans les propriétes de l'extender
            var extender = new DependencyFileStrategyModelExtender(ServiceProvider, item, Strategies);
            IGenerationStrategy strategy = GetStrategy(extender.StrategyName, item);
            if (strategy == null) // Pas de stratégie ou stratégie non trouvée
                return null; // TODO (a voir) Stratégie par defaut 

            // Initialisation de la stratégie
            // Différe si on est sur un modéle ou pas                
            if (store != null)
            {
                // Sur un modéle, on prend le store
                sp = store;
            }

            if (strategy.Initialize(host, sp, item))
                return strategy;

            return null;
        }

        /// <summary>
        /// Find a strategy by its name
        /// </summary>
        /// <param name="strategyName"></param>
        /// <returns></returns>
        private IGenerationStrategy GetStrategy(string strategyName, IVsProjectFileItem item)
        {
            System.Diagnostics.Contracts.Contract.Requires(item != null);
            if (String.IsNullOrWhiteSpace(strategyName))
                return null;
            var v = Strategies.FirstOrDefault(s => s.Metadata.Name.Equals(strategyName, StringComparison.InvariantCultureIgnoreCase));
            string ext = Path.GetExtension(item.Name);
            if (v != null)
            {
                if (v.Metadata.Extension != null && v.Metadata.Extension.Equals(ext, StringComparison.InvariantCultureIgnoreCase) == false)
                {
                    return null; // TODO avertir que la stratégie ne correspond pas à l'extension
                }

                return v.Value;
            }

            return null;
        }

        /// <summary>
        /// Initialize called when the associated document is open
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="project"></param>
        /// <returns></returns>
        public bool Activate(IStrategiesHost host, IServiceProvider serviceProvider)
        {
            System.Diagnostics.Contracts.Contract.Requires(host != null);
            System.Diagnostics.Contracts.Contract.Requires(serviceProvider != null);
            this.ServiceProvider = serviceProvider;
            this.host = host;

            if (IsExtenderProviderInitialized)
                return false;

            return InitializeExtenders();
        }

        /// <summary>
        /// Initialisation des extenders associés à cette stratégie (La 1ere fois uniquement)
        /// </summary>
        /// <returns></returns>
        private bool InitializeExtenders()
        {
            // Si dèja initialisé, ce n'est pas la peine de continuer
            if (IsExtenderProviderInitialized)
                return false;

            IsExtenderProviderInitialized = true;

            StrategyExtenderProvider extenderProvider = new DependencyFileStrategyT4ExtenderProvider(this);
            AddExtenderProvider(extenderProvider);
            extenderProvider = new DependencyFileStrategyModelExtenderProvider(this);
            AddExtenderProvider(extenderProvider);
            return true;
        }

        protected void AddExtenderProvider(StrategyExtenderProvider extenderProvider)
        {
            var extensionManager = ServiceProvider.GetService(typeof(EnvDTE.ObjectExtenders)) as EnvDTE.ObjectExtenders;
            extenderProviderCookies.Add(extensionManager.RegisterExtenderProvider(VSConstants.CATID.CSharpFileProperties_string, extenderProvider.Name, extenderProvider));
            extenderProviderCookies.Add(extensionManager.RegisterExtenderProvider(VSConstants.CATID.ProjectItemAutomationObject_string, extenderProvider.Name, extenderProvider));
        }
        
        /// <summary>
        /// The solution is closed
        /// </summary>
        /// <param name="serviceProvider"></param>
        public void Desactivate(IServiceProvider serviceProvider)
        {
            System.Diagnostics.Contracts.Contract.Requires(serviceProvider != null);

            // Désactivation des extenders lors de la fermeture de la solution
            if (!IsExtenderProviderInitialized)
                return;

            IsExtenderProviderInitialized = false;
            var extensionManager = serviceProvider.GetService(typeof(EnvDTE.ObjectExtenders)) as EnvDTE.ObjectExtenders;
            extenderProviderCookies.ForEach(c => { try { extensionManager.UnregisterExtenderProvider(c); } catch { } });
            extenderProviderCookies.Clear();
        }
    }
}
